#include "triangle_app.hpp"

#include <glm/ext/matrix_clip_space.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <glm/gtx/perpendicular.hpp>

struct TriangleData {
    glm::vec2 pos;
    glm::vec3 color;
};

static TriangleData triangle_data[]{
    {{ 0.f, -0.9f }, { 1.f, 0.f, 0.f } },
    {{ 0.7794f, 0.45f }, { 1.f, 1.f, 0.f } },
    {{ -0.7794f, 0.45f }, { 1.f, 0.f, 1.f } },
    {{ 0.f, 0.9f }, { 0.f, 1.f, 0.f } },
    {{ 0.7794f, -0.45f }, { 0.f, 0.f, 1.f } },
    {{ -0.7794f, -0.45f }, { 0.f, 1.f, 1.f } },
};

TriangleApp::TriangleApp(GLFWwindow *w, const char* name) : App(w, name),
                                          m_program_(nullptr),
                                          m_radius_(1.65f),
                                          m_enable_clip_(true),
                                          m_rotate_rpm_(10),
                                          m_rotate_degrees_(0)
{
}

TriangleApp::~TriangleApp() {

}

void TriangleApp::OnInitialize() {
    App::OnInitialize();
    m_program_ = GL::Program::Create("triangle");

    m_vao_ = GL::VertexArray::Create(1);
    m_vao_->Bind();
    {
        // config vao
        auto buffer = m_vao_->Get(0);
        buffer->Bind(GL_ARRAY_BUFFER);
        buffer->Data(GL_ARRAY_BUFFER, sizeof(triangle_data), triangle_data, GL_STATIC_DRAW);

        GLCallValidation(glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(TriangleData), (void *) 0));
        GLCallValidation(glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(TriangleData), (void *) offsetof(TriangleData, color)));
        GLCallValidation(glEnableVertexAttribArray(0));
        GLCallValidation(glEnableVertexAttribArray(1));
    }
    m_vao_->UnBind();

    // initialize camera
    m_camera_.position = glm::vec3(0.f, 0.f, 3.f);
    m_camera_.lookAt = glm::vec3(0.f, 0.f, 0.f);

    // load texture
    m_image_ = GL::Texture::Create("cupcakes.jpg");
}

void TriangleApp::OnCleanup() {
    App::OnCleanup();
}

void TriangleApp::RenderFrame(double delta_time, double total_time) {
    // update camera
//    m_camera_.Update(delta_time);

    m_program_->Use();

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR);

    m_rotate_degrees_ = (float)std::fmod(m_rotate_degrees_ - delta_time * m_rotate_rpm_ * 360 / 60, 360.0);
    // calculate matrix
    glm::mat4 model{ 1.f };
    model = glm::rotate(model, static_cast<float>(glm::radians(m_rotate_degrees_)), glm::vec3(0.f, 0.f, 1.f));

    glm::mat4 view{ 1.f };
    view = glm::lookAt(m_camera_.position, m_camera_.lookAt, glm::vec3(0.f, 1.f, 0.f));

    glm::mat4 projection{ 1.f };
    projection = glm::perspective(glm::radians(35.f), (float) m_window_width_ / (float) m_window_height_, 1.f, 100.f);

    auto mvp = projection * view * model;
    m_program_->UniformMat4f("mvp", mvp);
    m_program_->Uniform1f("radius", m_radius_);
    m_program_->Uniform1i("enable_clip", m_enable_clip_ ? 1 : 0);

    m_vao_->Bind();

    GLCallValidation(glDrawArrays(GL_TRIANGLES, 0, 6));

    m_vao_->UnBind();

    m_program_->NotUse();
}

void TriangleApp::OnKey(int key, int modifiers, bool is_press) {
    App::OnKey(key, modifiers, is_press);

    //    switch (key) {
    //        case GLFW_KEY_W:
    //            m_camera_.Dir(Camera::MD_FORWARD, is_press);
    //            break;// move forward
    //        case GLFW_KEY_S:
    //            m_camera_.Dir(Camera::MD_BACKWARD, is_press);
    //            break;// move backward
    //        case GLFW_KEY_A:
    //            m_camera_.Dir(Camera::MD_LEFT, is_press);
    //            break;// move left
    //        case GLFW_KEY_D:
    //            m_camera_.Dir(Camera::MD_RIGHT, is_press);
    //            break;// move move right
    //        case GLFW_KEY_EQUAL:
    //        case GLFW_KEY_KP_ADD:
    //            if (is_press) {
    //                if (modifiers & GLFW_MOD_CONTROL)
    //                    m_radius_ += 0.01f;
    //                else
    //                    m_radius_ += 0.1f;
    //                LOG_I("Radius = %.2f", m_radius_);
    //            }
    //            break;
    //        case GLFW_KEY_MINUS:
    //        case GLFW_KEY_KP_SUBTRACT:
    //            if (is_press) {
    //                if (modifiers & GLFW_MOD_CONTROL)
    //                    m_radius_ -= 0.01f;
    //                else
    //                    m_radius_ -= 0.1f;
    //                LOG_I("Radius = %.2f", m_radius_);
    //            }
    //            break;
    //        default:
    ////            LOG_I("Key %d %s", key, is_press ? "Pressed" : "Released");
    //            break;
    //    }
}

void TriangleApp::OnWheelScroll(int dx, int dy) {
    App::OnWheelScroll(dx, dy);

    // dx always be 0
    //    m_camera_.Rotate(dy);
}
void TriangleApp::RenderConfigUI() {
    App::RenderConfigUI();
    if (ImGui::TreeNodeEx("三角形配置", ImGuiTreeNodeFlags_Framed)) {
        ImGui::Columns(2);
        ImGui::TextUnformatted("旋转角度：");
        ImGui::NextColumn();
        ImGui::Text("%.4f", m_rotate_degrees_);
        ImGui::NextColumn();
        ImGui::TextUnformatted("启用裁剪：");
        ImGui::NextColumn();
        ImGui::Checkbox("##启用裁剪：", &m_enable_clip_);
        ImGui::NextColumn();
        ImGui::TextUnformatted("裁剪半径：");
        ImGui::NextColumn();
        ImGui::DragFloat("##裁剪半径：", &m_radius_, 0.01, 0, 10, "%.3f", 0);
        ImGui::NextColumn();
        ImGui::TextUnformatted("转速(RPM):");
        ImGui::NextColumn();
        ImGui::DragFloat("##转速(RPM):", &m_rotate_rpm_, 10, -20000, 20000, "%.2f");
        ImGui::Columns(1);
        ImGui::Vector3Editor("相机位置", m_camera_.position, glm::vec3{0.f, 0.f, 3.f}, 0.05f);
        ImGui::Vector3Editor("看向哪里", m_camera_.lookAt, glm::vec3{0.f, 0.f, 0.f}, 0.05f);
        // ImGui::Separator();
        // ImGui::Image((ImTextureID)m_image_->TexID(), ImVec2(m_image_->Width(), m_image_->Height()));
        ImGui::TreePop();
    }
}
//
//void Camera::Update(double delta_time) {
//    int ub = 0;
//    int lr = 0;
//
//    if (move_dir & MD_FORWARD)
//        ub += 1;
//    if (move_dir & MD_BACKWARD)
//        ub -= 1;
//    if (move_dir & MD_LEFT)
//        lr -= 1;
//    if (move_dir & MD_RIGHT)
//        lr += 1;
//
//    if (!ub && !lr)
//        return;
//
//    constexpr float speed = 5.0;
//    glm::vec3 dir = (float) ub * glm::normalize(direction);
//    glm::vec3 lr_vec = glm::cross(glm::vec3(0.f, 1.f, 1.f), direction);
//    dir += (float) lr * glm::normalize(lr_vec);
//
//    // update position
//    const auto translated = glm::translate(glm::mat4(1.f), dir * speed * (float) delta_time);
//    position = translated * glm::vec4(position, 1.f);
//}
//
//void Camera::Dir(MoveDir dir, bool enable) {
//    if (enable)
//        move_dir |= dir;
//    else
//        move_dir &= ~dir;
//}
//
//void Camera::Rotate(int degree) {
//    // 计算法线
//    auto dir = glm::cross(direction, glm::vec3(0.f, 1.f, 1.f));
//    direction = glm::rotate(glm::mat4(1.f), (float) glm::radians((float) degree), dir) * glm::vec4(direction, 1);
//    // LOG_I("Direction = {%.2f, %.2f, %.2f}", direction.x, direction.y, direction.z);
//}

DEFINE_FACTORY_FUNC(CreateTriangleApp, window, args) {
    (void) args;
    return std::make_unique<TriangleApp>(window, "Triangle");
}

REGISTER_APP_CREATOR("TriangleApp", &CreateTriangleApp);
